#include "abstractellipsegraphicitem.h"

#include "../../documents/data/graphicdata.h"
#include "../../undo/basicitemcommands.h"
#include "../../utils/undomanager.h"
#include "connectionpoint.h"
#include "graphicitemcommon.h"

#include <cmath>
#include <idocumentdata.h>
#include <ieditor.h>
#include <igraphicitemcontent.h>
#include <limits>
#include <serializationengine.h>

#include <QtCore/QRegExp>
#include <QtCore/QVariantList>
#include <QtGui/QGraphicsBlurEffect>
#include <QtGui/QGraphicsScene>
#include <QtGui/QPainter>
#include <QtGui/QGraphicsSceneHoverEvent>
#include <QtGui/QStyleOptionGraphicsItem>
#include <QtGui/QTransform>

using namespace core;
using namespace swp;

AbstractEllipseGraphicItem::AbstractEllipseGraphicItem(
    QGraphicsItem* parent) : EllipseGraphicItem(true, false, parent),
        IGraphicEditorItem(),
        m_id(QUuid::createUuid()),
        m_isInConnectMode(false),
        m_lastPosition(),
        m_rect(),
        m_content(0),
        m_contentAlignment(Qt::AlignCenter),
        m_thumbnail(PREFERRED_THUMBNAIL_SIZE, PREFERRED_THUMBNAIL_SIZE),
        m_selectionHighlight(0),
        m_connectHighlight(0),
        m_transformMatrix(0),
        m_connectPoints(),
        m_connectPointItems(),
        m_parentItem(0),
        m_documentData(0),
        m_connections(),
        m_connectionToConnectorMapping(),
        m_palette(0),
        m_node(0)
{
    createBasicLookAndFeel();
}

AbstractEllipseGraphicItem::AbstractEllipseGraphicItem(
    const QRectF& rect, IGraphicItemContent* content,
    QGraphicsItem* parent) : EllipseGraphicItem(rect, true, false, parent),
        IGraphicEditorItem(),
        m_id(QUuid::createUuid()),
        m_isInConnectMode(false),
        m_lastPosition(),
        m_rect(rect),
        m_content(content),
        m_contentAlignment(Qt::AlignCenter),
        m_thumbnail(PREFERRED_THUMBNAIL_SIZE, PREFERRED_THUMBNAIL_SIZE),
        m_selectionHighlight(0),
        m_connectHighlight(0),
        m_transformMatrix(0),
        m_connectPoints(),
        m_connectPointItems(),
        m_parentItem(0),
        m_documentData(0),
        m_connections(),
        m_connectionToConnectorMapping(),
        m_palette(0),
        m_node(0)
{
    if (m_content != 0)
    {
        m_content->graphicsItem()->setParentItem(this);
    }

    setRect(rect);

    createBasicLookAndFeel();
}

AbstractEllipseGraphicItem::~AbstractEllipseGraphicItem()
{

}

void AbstractEllipseGraphicItem::setRectF(const QRectF& rect)
{
    if (m_rect != rect)
    {
        setRect(rect);

        m_rect = rect;

        if (m_content != 0)
        {
            refreshContentPosition();
        }
    }
}

QPixmap AbstractEllipseGraphicItem::thumbnail(ThumbnailRole role,
    int size) const
{
    const int _size = size == -1 ? PREFERRED_THUMBNAIL_SIZE : size;

    m_thumbnail = QPixmap(_size, _size);
    m_thumbnail.fill(Qt::transparent);
    QPainter painter(&m_thumbnail);
    if (antialiasing())
    {
        painter.setRenderHint(QPainter::Antialiasing);
    }

    QRectF thumbnailRect = QRectF(0, 0,
        _size - PREFERRED_THUMBNAIL_PADDING -
        PREFERRED_THUMBNAIL_PEN_SIZE,
        _size - PREFERRED_THUMBNAIL_PADDING -
        PREFERRED_THUMBNAIL_PEN_SIZE);

    const qreal coefficient =  qMin(
        thumbnailRect.width() / m_rect.width(),
        thumbnailRect.height() / m_rect.height());

    QTransform transform;
    transform.scale(coefficient, coefficient);
    QRectF transformed = transform.mapRect(m_rect);

    QRectF _transformed = transformed.translated(
        (_size - transformed.width()) / 2,
        (_size - transformed.height()) / 2);

    painter.setPen(pen());
    painter.setBrush(brush());
    painter.drawEllipse(_transformed);

    if (m_content != 0)
    {
        m_content->drawThumbnail(painter, _transformed);
    }

    return m_thumbnail;
}

void AbstractEllipseGraphicItem::editorModeChange()
{
    GraphicData* _scene = dynamic_cast<GraphicData* >(scene());

    m_isInConnectMode = false;

    if (_scene != 0)
    {
        switch (_scene->currentState())
        {
            case IEditor::SelectMove:
                deleteTransformationMatrix();
                break;
            case IEditor::Transform:
                createTransformationMatrix();
                break;
            case IEditor::Connect:
                m_isInConnectMode = true;
                break;
            case IEditor::Edit:
                break;
            default:
                Q_ASSERT(false); // shouldn't ever happen
        }
    }

    checkConnectHighlight();
    checkConnectPoints();
}

void AbstractEllipseGraphicItem::setContent(IGraphicItemContent* content)
{
    if (content != 0)
    {
        delete m_content;

        m_content = content;
        m_content->graphicsItem()->setParentItem(this);

        refreshContentPosition();
    }
}

IGraphicItemContent* AbstractEllipseGraphicItem::content() const
{
    return m_content;
}

void AbstractEllipseGraphicItem::setPalette(const Palette* const palette)
{
    m_palette = palette;

    refreshPalette();
}

int AbstractEllipseGraphicItem::paletteType() const
{
    return Background | Border;
}

void AbstractEllipseGraphicItem::scaleToFitContent()
{
    const qreal coefficient =  m_content == 0 ? 1 : qMax(
        m_content->graphicsItem()->boundingRect().width() /
            m_rect.width(),
                m_content->graphicsItem()->boundingRect().height() /
                    m_rect.height());

    QTransform transform;
    transform.scale(coefficient, coefficient);
    QRectF transformed = transform.mapRect(m_rect);

    if (m_connectPoints.empty())
    {
        m_connectPoints = connectPoints();
    }

    QPolygonF connectPointsTransformed = transform.map(
        QPolygonF(m_connectPoints.toVector()));
    m_connectPoints = connectPointsTransformed.toList();

    setRectF(transformed);

    refreshAuxiliaryLayers();
    updateConnectionPositions();
}

void AbstractEllipseGraphicItem::handleContentRefresh()
{
    refreshContentPosition();
}

void AbstractEllipseGraphicItem::handleConnectorPositionChange()
{
    foreach (const core::ConnectionData& data, m_connections)
    {
        Q_ASSERT(data.Connector != 0);
        m_connectionToConnectorMapping[data] = connectorForConnection(data);
        /*
        AbstractLineGraphicItem* connector =
            dynamic_cast<AbstractLineGraphicItem*>(data.Connector);

        Q_ASSERT(connector != 0 &&
            m_connectionToConnectorMapping.contains(data));
        // type mismatch && not mapped connection

        if (data.From == this)
        {
            connector->setStartPoint(nearestConnectionPoint(connector->startPoint()));
        }
        else
        {
            connector->setStartPoint(nearestConnectionPoint(connector->endPoint()));
        }*/ // commented until this function will be called correctly
    }
}

void AbstractEllipseGraphicItem::handleSelfConnect(
    IGraphicEditorItem* connector, const QPointF& cursorPos,
    bool previousSelfConnect)
{
    if (!previousSelfConnect)
    {
        const bool valid = supportsConnection(
            m_documentData != 0 ? m_documentData->connectionData() :
                ConnectionData(CONNECTION_NOT_VALID));

        deleteHighlight();
        deleteConnectPoints();

        createHighlight(Connect, valid);
        createConnectPoints(valid);
    }

    QPointF target = nearestConnectionPoint(cursorPos, Self);
    target = mapFromScene(target.x(), target.y());

    AbstractLineGraphicItem* _connector =
        dynamic_cast<AbstractLineGraphicItem*>(connector);

    const QPointF origin = mapFromScene(_connector->startPoint().x(),
        _connector->startPoint().y());

    QList<QPointF> _connectPoints = m_connectPoints;

    const int originIndex = _connectPoints.indexOf(origin);
    const int targetIndex = _connectPoints.indexOf(target);

    const QList<QPointF> potentialAnchors = selfConnectAnchorPoints();

    _connector->removeAnchorPoints();

    if (originIndex != targetIndex)
    {
        int fwJumps = 0;

        int it = originIndex;

        while (it != targetIndex)
        {
            if ((it + 1) == m_connectPoints.size())
            {
                it = -1;
            }

            ++fwJumps;
            ++it;
        }

        int bwJumps = 0;

        it = originIndex;

        while (it != targetIndex)
        {
            if ((it - 1) == -1)
            {
                it = m_connectPoints.size();
            }

            ++bwJumps;
            --it;
        }

        const bool goForward = fwJumps <= bwJumps;

        const int anchorOriginIndex = anchorIndex(originIndex);
        const int anchorTargetIndex = anchorIndex(targetIndex);

        for (int i = anchorOriginIndex; i != anchorTargetIndex;
            goForward ? ++i : --i)
        {
            const QPointF anchor = potentialAnchors[i];
            _connector->addAnchorPoint(mapToScene(anchor.x(), anchor.y()));

            if (goForward)
            {
                if ((i + 1) == potentialAnchors.size())
                {
                    i = -1;
                }
            }
            else
            {
                if ((i - 1) == -1)
                {
                    i = potentialAnchors.size();
                }
            }
        }

        const QPointF anchor = potentialAnchors[anchorTargetIndex];
        _connector->addAnchorPoint(mapToScene(anchor.x(), anchor.y()));
    }
    else
    {
        const int anchorOriginIndex = anchorIndex(originIndex);
        const int anchorTargetIndex =
            (anchorOriginIndex + 1) == potentialAnchors.size() ? 0 :
            (anchorOriginIndex + 1);

        const QPointF fAnchor = potentialAnchors[anchorOriginIndex];
        const QPointF sAnchor = potentialAnchors[anchorTargetIndex];

        _connector->addAnchorPoint(mapToScene(fAnchor.x(), fAnchor.y()));
        _connector->addAnchorPoint(mapToScene(sAnchor.x(), sAnchor.y()));
    }
}

void AbstractEllipseGraphicItem::ensureHandlesGeometryChanges(bool enabled)
{
    Q_ASSERT(m_documentData != 0); // shouldn't be null

    GraphicData* graphicData = dynamic_cast<GraphicData*>(m_documentData);

    Q_ASSERT(graphicData != 0); // shouldn't be null

    setFlag(ItemSendsGeometryChanges,
        m_connections.size() > 0 ? true :
            (false || graphicData->snapToGridEnabled()));
}

QPointF AbstractEllipseGraphicItem::nearestConnectionPoint(
    const QPointF& to, ConnectionDirection direction) const
{
    QPointF ret;

    if (m_connectPoints.empty())
    {
        m_connectPoints = connectPoints();
    }

    const QList<QPointF> conPointsForIn = connectPointsForIncoming();
    const QList<QPointF> conPointsForOut = connectPointsForOutgoing();

    QList<QPointF> _connectPoints;
    if (conPointsForIn.size() == 0 && conPointsForOut.size() == 0)
    {
        _connectPoints = m_connectPoints;
    }
    else
    {
        _connectPoints = direction == Self ? conPointsForIn + conPointsForOut
            : (direction == Incoming ? conPointsForIn : conPointsForOut);
    }

    qreal manhattanLength = std::numeric_limits<qreal>::max();

    foreach (const QPointF& point, _connectPoints)
    {
        const QPointF potential = (point + scenePos());
        QPointF d = potential - to;

        if (d.manhattanLength() < manhattanLength)
        {
            ret = potential;
            manhattanLength = d.manhattanLength();
        }
    }

    return ret;
}

void AbstractEllipseGraphicItem::handleSceneResize()
{

}

void AbstractEllipseGraphicItem::arrangeConnectorsRequest()
{
    updateConnectionPositions();
}

bool AbstractEllipseGraphicItem::canBeDuplicated() const
{
    return true;
}

QList<ConnectionData> AbstractEllipseGraphicItem::connections() const
{
    QList<ConnectionData> ret;

    Q_ASSERT(m_documentData != 0); // shouldn't be null

    GraphicData* graphicData = dynamic_cast<GraphicData*>(m_documentData);

    Q_ASSERT(graphicData != 0); // shouldn't be null

    ret = graphicData->connections(this);

    return ret;
}

void AbstractEllipseGraphicItem::connectionsRefreshed()
{
    m_connectionToConnectorMapping.clear();
    m_connections = connections();

    foreach (const core::ConnectionData& data, m_connections)
    {
        Q_ASSERT(data.Connector != 0);
        m_connectionToConnectorMapping[data] = connectorForConnection(data);

        AbstractLineGraphicItem* connector =
            dynamic_cast<AbstractLineGraphicItem*>(data.Connector);

        Q_ASSERT(connector != 0 &&
            m_connectionToConnectorMapping.contains(data));
        // type mismatch && not mapped connection

        if (data.From == this)
        {
            connector->setStartShape(pureShape());
        }
        else
        {
            connector->setEndShape(pureShape());
        }
    }

    Q_ASSERT(m_documentData != 0); // shouldn't be null

    GraphicData* graphicData = dynamic_cast<GraphicData*>(m_documentData);

    Q_ASSERT(graphicData != 0); // shouldn't be null

    setFlag(ItemSendsGeometryChanges,
        m_connections.size() > 0 ? true :
            (false || graphicData->snapToGridEnabled()));
}

IEditorItem* AbstractEllipseGraphicItem::parentEditorItem(
    IEditorItem* parent) const
{
    return m_parentItem;
}

void AbstractEllipseGraphicItem::setParentEditorItem(
    IEditorItem* parent)
{
    m_parentItem = parent;
}

IDocumentData* AbstractEllipseGraphicItem::parentDocumentData() const
{
    return m_documentData;
}

void AbstractEllipseGraphicItem::setParentDocumentData(IDocumentData* data)
{
    m_documentData = data;
}

bool AbstractEllipseGraphicItem::isModel() const
{
    return false;
}

bool AbstractEllipseGraphicItem::isGraphic() const
{
    return true;
}

bool AbstractEllipseGraphicItem::isText() const
{
    return false;
}
QUuid AbstractEllipseGraphicItem::id() const
{
    return m_id;
}

bool AbstractEllipseGraphicItem::doActionForProperty(
    const QString& property, const QVariantList& value,
    const core::SerializationEngine& helper)
{
    bool ret = true;

    if (property == "id")
    {
        m_id = QUuid(value.first().toString());
    }
    else if (property == "position")
    {
        setPos(value.first().toPointF());
    }
    else if (property == "zvalue")
    {
        setZValue(value.first().toDouble());
    }
    else if (property == "rect")
    {
        QRectF rect(value[0].toPointF(), value[1].toPointF());

        const qreal coefficient = qMax(
            m_rect.width() /
                rect.width(),
                    m_rect.height() /
                        rect.height());

        QTransform transform;
        transform.scale(coefficient, coefficient);
        transform = transform.inverted();

        if (m_connectPoints.empty())
        {
            m_connectPoints = connectPoints();
        }

        QPolygonF connectPointsTransformed = transform.map(
            QPolygonF(m_connectPoints.toVector()));
        m_connectPoints = connectPointsTransformed.toList();

        setRectF(rect);
    }
    else if (property == "content")
    {
        if (m_content != 0)
        {
            ret = helper.deserialize(value.first().toString(), m_content);
        }
    }

    return ret;
}

void AbstractEllipseGraphicItem::deserializationFinished(bool ok)
{
    // do nothing
}

QHash<QString, QVariantList> AbstractEllipseGraphicItem::serializationData(
    const core::SerializationEngine& helper) const
{
    QHash<QString, QVariantList> ret;

    QVariantList list;

    list.append(QVariant(id().toString()));

    ret["id"] = list;

    list.clear();

    list.append(QVariant(scenePos()));

    ret["position"] = list;

    list.clear();

    list.append(QVariant(zValue()));
    ret["zvalue"] = list;

    list.clear();

    list.append(m_rect.topLeft());
    list.append(m_rect.bottomRight());

    ret["rect"] = list;

    list.clear();

    if (m_content != 0)
    {
        bool proper = false;
        const QString serializedContent = helper.serialize(m_content,
            &proper);

        Q_ASSERT(proper); // should pass

        list.append(QVariant(serializedContent));

        ret["content"] = list;
    }    

    return ret;
}

void AbstractEllipseGraphicItem::registerNode(core::Node* node)
{
    m_node = node;
}

const Node* const AbstractEllipseGraphicItem::node() const
{
    return m_node;
}

QRectF AbstractEllipseGraphicItem::boundingRect() const
{
    QRectF ret = EllipseGraphicItem::boundingRect();

    foreach (QGraphicsEllipseItem* item, m_connectPointItems)
    {
        const QPointF pos = item->pos();
        const QRectF rect = item->boundingRect();
        const QRectF itemRect(pos.x(), pos.y(),
            rect.width(), rect.height());
        ret = ret.united(itemRect);
    }

    return ret;
}

QPainterPath AbstractEllipseGraphicItem::shape() const
{
    QPainterPath ret = QGraphicsEllipseItem::shape();

    foreach (QGraphicsEllipseItem* item, m_connectPointItems)
    {
        const QPointF pos = item->pos();
        const QRectF rect = item->boundingRect();
        const QRectF itemRect(pos.x(), pos.y(),
            rect.width(), rect.height());
        ret.addEllipse(itemRect);
    }

    return ret;
}

int AbstractEllipseGraphicItem::type() const
{
    return ABS_ELLIPSE_ITEM;
}

QVariant AbstractEllipseGraphicItem::itemChange(GraphicsItemChange change,
        const QVariant &value)
{
    QVariant _value = value;

    if (change == QGraphicsItem::ItemSelectedHasChanged)
    {
        GraphicData* _scene = dynamic_cast<GraphicData* >(scene());

        if (value.toBool())
        {
            if (_scene != 0)
            {
                createHighlight(Selection);

                if (_scene->currentState() == IEditor::SelectMove)
                {
                    deleteTransformationMatrix();
                }
                else if (_scene->currentState() == IEditor::Transform)
                {
                    createTransformationMatrix();
                }
            }
        }
        else
        {
            deleteHighlight();
            deleteTransformationMatrix();
        }
    }

    if (change == QGraphicsItem::ItemPositionChange ||
        change == QGraphicsItem::ItemPositionHasChanged)
    {
        const QPointF newPos = _value.toPointF();

        updateConnectionPositions();

        GraphicData* _scene = dynamic_cast<GraphicData* >(scene());

        if (_scene != 0 && _scene->snapToGridEnabled())
        {
            const QPointF adjustedPosition =
                _scene->snapToGrid(this, pos(), newPos);

            if (newPos != adjustedPosition)
            {
                _value = QVariant(adjustedPosition);
            }
        }

        const QPointF newScenePos = mapToScene(_value.toPointF());

        if (newScenePos.x() < 0 || newScenePos.y() < 0)
        {
            _value = QVariant(mapFromScene(
                newScenePos.x() < 0 ? 0 : newScenePos.x(),
                newScenePos.y() < 0 ? 0 : newScenePos.y()));
        }

        if (_scene != 0)
        {
            _scene->expandIfNeeded(this, _value.toPointF());
        }
    }

    if (change == QGraphicsItem::ItemPositionHasChanged)
    {
        foreach (const core::ConnectionData& data, m_connections)
        {
            AbstractLineGraphicItem* connector =
                dynamic_cast<AbstractLineGraphicItem*>(data.Connector);

            Q_ASSERT(connector != 0 &&
                m_connectionToConnectorMapping.contains(data));
            // type mismatch && not mapped connection

            if (data.From == this)
            {
                connector->setStartShape(pureShape());
            }
            else
            {
                connector->setEndShape(pureShape());
            }
        }
    }

    return QGraphicsEllipseItem::itemChange(change, _value);
}

void AbstractEllipseGraphicItem::refreshAuxiliaryLayers()
{
    deleteTransformationMatrix();

    recreateSelectHighlight();

    GraphicData* _scene = dynamic_cast<GraphicData* >(scene());
    if (_scene != 0 && _scene->currentState() == IEditor::Transform)
    {
        createTransformationMatrix();
    }
}

QList<QPointF> AbstractEllipseGraphicItem::connectPointsForIncoming() const
{
    QList<QPointF> ret;

    return ret;
}

QList<QPointF> AbstractEllipseGraphicItem::connectPointsForOutgoing() const
{
    QList<QPointF> ret;

    return ret;
}

QList<int> AbstractEllipseGraphicItem::selfConnectAnchorMappings(
    ) const
{
    QList<int> ret;

    return ret;
}

void AbstractEllipseGraphicItem::setContentAlignment(int alignment)
{
    m_contentAlignment = alignment;
}

void AbstractEllipseGraphicItem::mousePressEvent(
    QGraphicsSceneMouseEvent* event)
{
    m_lastPosition = pos();

    QGraphicsEllipseItem::mousePressEvent(event);
}

void AbstractEllipseGraphicItem::mouseReleaseEvent(
    QGraphicsSceneMouseEvent* event)
{
    QGraphicsEllipseItem::mouseReleaseEvent(event);

    if (pos() != m_lastPosition)
    {
        GraphicData* _scene = dynamic_cast<GraphicData* >(scene());

        if (_scene != 0)
        {
            QUndoCommand* command = 0;
            if (_scene->selectedGraphicItems().size() == 1)
            {
                command = new ItemMoveCommand(_scene, this,
                    m_lastPosition, pos());
            }
            else
            {
                command = new ItemsMoveCommand(_scene, this,
                    _scene->selectedGraphicItems(), m_lastPosition, pos());
            }

            UndoManager::getInstance()->push(_scene, command);

            _scene->emitDataChanged();
        }
    }
}

void AbstractEllipseGraphicItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
    const bool valid = supportsConnection(
        m_documentData != 0 ? m_documentData->connectionData() :
            ConnectionData(CONNECTION_NOT_VALID));

    if (m_isInConnectMode && showsConnectionHighlight()
        && m_connectHighlight == 0)
    {
        deleteHighlight();

        createHighlight(Connect, valid);
    }

    if (m_isInConnectMode && m_connectPointItems.empty())
    {
        createConnectPoints(valid);
    }
}

void AbstractEllipseGraphicItem::hoverMoveEvent(
    QGraphicsSceneHoverEvent *event)
{
    if (m_isInConnectMode &&
        event->modifiers() == Qt::AltModifier)
    {
        QPointF point = event->scenePos();

        foreach (ConnectionPoint* _point, m_connectPointItems)
        {
            QPointF temp;
            if (_point->isInGravitationField(event->scenePos(), temp))
            {
                point = temp;
                break;
            }
        }

        GraphicData* _scene = dynamic_cast<GraphicData* >(scene());

        if (_scene != 0 && point != event->scenePos())
        {
            _scene->snapCursor(point);
        }
    }

    if (!m_isInConnectMode && m_connectHighlight != 0)
    {
        recreateSelectHighlight();
    }
}

void AbstractEllipseGraphicItem::hoverLeaveEvent(
    QGraphicsSceneHoverEvent *event)
{
    if (m_connectHighlight != 0)
    {
        recreateSelectHighlight();
    }

    deleteConnectPoints();
}

bool AbstractEllipseGraphicItem::showsConnectionHighlight() const
{
    return true;
}

void AbstractEllipseGraphicItem::createBasicLookAndFeel()
{
    QPen _pen = pen();
    _pen.setCosmetic(true);
    setPen(_pen);
    setBrush(QBrush(Qt::white, Qt::SolidPattern));
    setAcceptsHoverEvents(true);
    setFlag(ItemSendsGeometryChanges, true);
}

void AbstractEllipseGraphicItem::createHighlight(HighlightType type,
    bool valid)
{
    switch (type)
    {
        case Selection:
            if (m_selectionHighlight == 0)
            {
                QGraphicsBlurEffect* effect = new QGraphicsBlurEffect();
                effect->setBlurHints(QGraphicsBlurEffect::PerformanceHint);
                effect->setBlurRadius(10);

                m_selectionHighlight = new QGraphicsEllipseItem(rect(),
                    this);
                m_selectionHighlight->setPen(QPen(Qt::transparent));
                m_selectionHighlight->setBrush(QBrush(m_palette != 0 ?
                    m_palette->selectionColor() : Qt::darkCyan));
                m_selectionHighlight->setGraphicsEffect(effect);
                m_selectionHighlight->setFlags(m_selectionHighlight->flags() |
                    QGraphicsItem::ItemStacksBehindParent);
            }
            break;
        case Connect:
            if (m_connectHighlight == 0)
            {
                QGraphicsBlurEffect* effect = new QGraphicsBlurEffect();
                effect->setBlurHints(QGraphicsBlurEffect::PerformanceHint);
                effect->setBlurRadius(10);

                m_connectHighlight = new QGraphicsEllipseItem(rect(),
                    this);
                m_connectHighlight->setPen(QPen(Qt::transparent));
                m_connectHighlight->setBrush(QBrush(m_palette != 0 ?
                    (valid ? m_palette->acceptsConnectionColor() :
                        m_palette->deniesConnectionColor()) :
                    (valid ? Qt::darkGreen : Qt::darkRed)));
                m_connectHighlight->setGraphicsEffect(effect);
                m_connectHighlight->setFlags(m_connectHighlight->flags() |
                    QGraphicsItem::ItemStacksBehindParent);
            }
            break;
    }
}

void AbstractEllipseGraphicItem::createTransformationMatrix()
{
    if (m_transformMatrix == 0)
    {
        m_transformMatrix = new QGraphicsEllipseItem(
            rect(), this);
        m_transformMatrix->setPen(QPen(m_palette != 0 ?
            m_palette->transformationColor() : Qt::darkBlue, 2));
        m_transformMatrix->setBrush(QBrush(Qt::transparent));
    }
}

void AbstractEllipseGraphicItem::createConnectPoints(bool valid)
{
    if (scene() != 0)
    {
        const int connectPointR = 10;

        if (m_connectPoints.empty())
        {
            m_connectPoints = connectPoints();
        }

        const QList<QPointF> points = m_connectPoints;

        foreach (QPointF point, points)
        {
            QPointF _point = point;
            _point.rx() -= connectPointR /2;
            _point.ry() -= connectPointR /2;
            ConnectionPoint* pointItem = new ConnectionPoint(
                0, 0, connectPointR,
                connectPointR, this);
            pointItem->setPos(_point);
            pointItem->setPen(QPen(Qt::black, 1));
            QBrush brush(valid ? Qt::darkGreen : Qt::darkRed);
            QColor color = brush.color();
            color.setAlpha(125);
            brush.setColor(color);
            pointItem->setBrush(brush);

            m_connectPointItems.append(pointItem);
        }
    }
}

void AbstractEllipseGraphicItem::deleteHighlight()
{
    if (m_selectionHighlight != 0 && scene() != 0)
    {
        scene()->removeItem(m_selectionHighlight);
    }

    if (m_connectHighlight != 0 && scene() != 0)
    {
        scene()->removeItem(m_connectHighlight);
    }

    delete m_selectionHighlight;
    m_selectionHighlight = 0;

    delete m_connectHighlight;
    m_connectHighlight = 0;
}

void AbstractEllipseGraphicItem::deleteTransformationMatrix()
{
    if (m_transformMatrix != 0 && scene() != 0)
    {
        scene()->removeItem(m_transformMatrix);
    }

    delete m_transformMatrix;
    m_transformMatrix = 0;
}

void AbstractEllipseGraphicItem::deleteConnectPoints()
{
    if (scene() != 0)
    {
        foreach (QGraphicsEllipseItem* item, m_connectPointItems)
        {
            scene()->removeItem(item);
        }

        m_connectPointItems.clear();
    }
}

void AbstractEllipseGraphicItem::recreateSelectHighlight()
{
    deleteHighlight();

    if (isSelected())
    {
        createHighlight(Selection);
    }
}

void AbstractEllipseGraphicItem::checkConnectHighlight()
{
    if (!m_isInConnectMode)
    {
        recreateSelectHighlight();
    }
}

void AbstractEllipseGraphicItem::checkConnectPoints()
{
    if (!m_isInConnectMode)
    {
        deleteConnectPoints();
    }
}

void AbstractEllipseGraphicItem::updateConnectionPositions()
{
    foreach (const ConnectionData& data, m_connections)
    {
        AbstractLineGraphicItem* connector =
            dynamic_cast<AbstractLineGraphicItem*>(data.Connector);

        Q_ASSERT(connector != 0 &&
            m_connectionToConnectorMapping.contains(data));
        // type mismatch && not mapped connection

        const int index = m_connectionToConnectorMapping[data];
        const QPointF position = scenePos() + m_connectPoints[index];

        if (data.From == this)
        {
            connector->setStartPoint(position);
        }
        else
        {
            connector->setEndPoint(position);
        }
    }
}

void AbstractEllipseGraphicItem::refreshContentPosition()
{
    if (m_content != 0)
    {
        qreal x = (m_rect.width() -
            m_content->croppedBoundingRect().width()) / 2;
        if ((m_contentAlignment & Qt::AlignLeft) == Qt::AlignLeft)
        {
            x = 0;
        }
        else if ((m_contentAlignment & Qt::AlignRight) == Qt::AlignRight)
        {
            x = m_rect.width() -
                m_content->croppedBoundingRect().width();
        }

        qreal y = (m_rect.height() -
            m_content->croppedBoundingRect().height()) / 2;
        if ((m_contentAlignment & Qt::AlignTop) == Qt::AlignTop)
        {
            y = 0;
        }
        else if ((m_contentAlignment & Qt::AlignBottom) == Qt::AlignBottom)
        {
            y = m_rect.height() -
                m_content->croppedBoundingRect().height();
        }

        m_content->graphicsItem()->setPos(x, y);
    }
}

void AbstractEllipseGraphicItem::refreshPalette()
{
    if (m_palette != 0)
    {
        const int type = paletteType();

        if ((type & Whole) == Whole)
        {
            setPen(QPen(Qt::transparent, 0));
            setBrush(QBrush(m_palette->wholeColor()));
        }
        else
        {
            if ((type & Border) == Border){
                QPen _pen = QPen(m_palette->borderColor(),
                    PREFERRED_PEN_SIZE);
                _pen.setCosmetic(true);
                setPen(_pen);
            } else {
                setPen(QPen(Qt::transparent, 0));
            }

            if ((type & Background) == Background){
                setBrush(QBrush(m_palette->backgroundColor()));
            } else {
                setBrush(QBrush(Qt::transparent));
            }
        }

        if (m_transformMatrix != 0)
        {
            m_transformMatrix->setPen(QPen(m_palette->transformationColor(),
                2));
        }

        if (m_selectionHighlight != 0)
        {
            m_selectionHighlight->setBrush(QBrush(
                m_palette->selectionColor()));
        }

        if (m_connectHighlight != 0)
        {
            const bool valid = supportsConnection(
                m_documentData != 0 ? m_documentData->connectionData() :
                    ConnectionData(CONNECTION_NOT_VALID));

            m_connectHighlight->setBrush(QBrush(valid ?
                m_palette->acceptsConnectionColor() :
                m_palette->deniesConnectionColor()));
        }
    }
}

int AbstractEllipseGraphicItem::connectorForConnection(
    const core::ConnectionData& data) const
{
    int ret;

    AbstractLineGraphicItem* connector =
        dynamic_cast<AbstractLineGraphicItem*>(data.Connector);

    Q_ASSERT(connector != 0); // type mismatch

    if (m_connectPoints.empty())
    {
        m_connectPoints = connectPoints();
    }

    const QPointF to = data.From == this ? connector->startPoint()
        : connector->endPoint();

    const ConnectionDirection direction = data.From == data.To ? Self :
        (data.From == this ? Outgoing : Incoming);

    QPointF _ret;

    const QList<QPointF> conPointsForIn = connectPointsForIncoming();
    const QList<QPointF> conPointsForOut = connectPointsForOutgoing();

    QList<QPointF> _connectPoints;
    if (conPointsForIn.size() == 0 && conPointsForOut.size() == 0)
    {
        _connectPoints = m_connectPoints;
    }
    else
    {
        _connectPoints = direction == Self ? conPointsForIn + conPointsForOut
            : (direction == Incoming ? conPointsForIn : conPointsForOut);
    }

    qreal manhattanLength = std::numeric_limits<qreal>::max();

    foreach (const QPointF& point, _connectPoints)
    {
        const QPointF potential = (point + scenePos());
        QPointF d = potential - to;

        if (d.manhattanLength() < manhattanLength)
        {
            _ret = potential - scenePos();
            manhattanLength = d.manhattanLength();
        }
    }

    ret = m_connectPoints.indexOf(_ret);

    return ret;
}

QPainterPath AbstractEllipseGraphicItem::pureShape() const
{
    QPainterPath ret = QGraphicsEllipseItem::shape().translated(scenePos());

    return ret;
}

int AbstractEllipseGraphicItem::anchorIndex(int index) const
{
    int ret = -1;

    const QList<int> indexMappings = selfConnectAnchorMappings();

    if (indexMappings.size() == 0)
    {
        ret = index;
    }
    else
    {
        ret = indexMappings[index];
    }

    return ret;
}
